package com.ruoyi.hikvision.hk;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.tool.ImgUtils;
import com.ruoyi.common.utils.tool.TimerUtils;
import com.ruoyi.hikvision.NetSDK.HCNetSDK;
import com.ruoyi.hikvision.hk.commom.ConfigFileUtil;
import com.ruoyi.hikvision.hk.commom.TransIsapi;
import com.ruoyi.hikvision.hk.entity.HiEmployee;
import com.ruoyi.hikvision.hk.entity.HiResponse;
import com.ruoyi.wisdom.constants.IotConstants;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HikvisionClient {

    private String localFaceUrl = IotConstants.LOCAL_FACE_URL;
    private final HCNetSDK hCNetSDK;
    private final String sIP;
    private final short sPort;
    private final String sUserName;
    private final String sPassword;
    private String deviceNo;
    private int mUserId = -1;
    private int mAlarmHandle = -1;
    private final HikvisionAlarmCallBack alarmCallBack = new HikvisionAlarmCallBack();

    public int getUserId() {
        return mUserId;
    }

    public void setDeviceNo(String deviceNo) {
        this.deviceNo = deviceNo;
    }

    public HikvisionClient(HCNetSDK sdk, String ip, short port, String username, String password) {
        this.hCNetSDK = sdk;
        this.sIP = ip;
        this.sPort = port;
        this.sUserName = username;
        this.sPassword = password;
    }

    // 开启长连接配置
    private int startRemoteConfig(int dwCommand, Pointer lpInBuffer, int dwInBufferLen) {
        int lHandler = hCNetSDK.NET_DVR_StartRemoteConfig(mUserId, dwCommand, lpInBuffer, dwInBufferLen, null, null);
        if (lHandler < 0) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StartRemoteConfig 失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StartRemoteConfig 接口调用成功!");
        }
        return lHandler;
    }

    // 开启长连接配置
    private int startRemoteConfig(String dwCommand, int remoteConfig) {
        HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);
        System.arraycopy(dwCommand.getBytes(), 0, ptrByteArray.byValue, 0, dwCommand.length());
        ptrByteArray.write();
        int lHandler = hCNetSDK.NET_DVR_StartRemoteConfig(mUserId, remoteConfig, ptrByteArray.getPointer(), dwCommand.length(), null, null);
        if (lHandler < 0) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StartRemoteConfig 失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StartRemoteConfig 接口调用成功!");
        }
        return lHandler;
    }

    // 发送和接收数据
    private int sendWithRecvRemoteConfig(int lHandler, Pointer lpInBuff, int dwInBuffSize, Pointer lpOutBuff, IntByReference pInt) {
        // dwOutBuffSize是输出缓冲区大小，需要自定义指定大小，如果接口报错错误码43.说明接收设备数据的缓冲区或存放图片缓冲区不足，应扩大缓冲区大小
        int dwState = hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(lHandler, lpInBuff, dwInBuffSize, lpOutBuff, 20 * 1024, pInt);
        if (dwState == -1) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_SendWithRecvRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_SendWithRecvRemoteConfig 接口调用成功，状态值：" + dwState);
        }
        return dwState;
    }

    // 关闭长连接配置
    private void stopRemoteConfig(int lHandler) {
        if (!hCNetSDK.NET_DVR_StopRemoteConfig(lHandler)) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StopRemoteConfig 接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] NET_DVR_StopRemoteConfig 接口调用成功");
        }
    }

    // 报警布防
    public void setupAlarmChan() {
        if (mUserId == -1) return;
        alarmCallBack.setsIP(sIP);
        alarmCallBack.setDeviceNo(deviceNo);
        if (!hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(alarmCallBack, null)) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] SetDVRMessageCallBack 设置回调函数失败错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] SetDVRMessageCallBack 设置回调函数成功!");
        }
        HCNetSDK.NET_DVR_SETUPALARM_PARAM param = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
        param.dwSize = param.size();
        // 布防优先级：0-一等级（高），1-二等级（中），2-三等级（低）
        param.byLevel = 0;
        // 智能交通报警信息上传类型：0-老报警信息（NET_DVR_PLATE_RESULT），1-新报警信息(NET_ITS_PLATE_RESULT)
        param.byAlarmInfoType = 1;
        // 布防类型：0-客户端布防，1-实时布防
        param.byDeployType = 1;
        param.write();
        mAlarmHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(mUserId, param);
        if (mAlarmHandle == -1) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] 设备布防失败，错误码:" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] 设备布防成功");
        }
    }

    // 报警撤防
    public void closeAlarmChan() {
        if (mAlarmHandle == -1) return;
        if (hCNetSDK.NET_DVR_CloseAlarmChan_V30(mAlarmHandle)) {
            System.out.println("[" + sIP + "] [" + deviceNo + "] 设备撤防成功");
        } else {
            System.err.println("[" + sIP + "] [" + deviceNo + "] 设备撤防失败，错误码:" + hCNetSDK.NET_DVR_GetLastError());
        }
    }

    // 登录设备
    public int login() {
        HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
        mUserId = hCNetSDK.NET_DVR_Login_V30(sIP, sPort, sUserName, sPassword, deviceInfo);
        if (mUserId == -1) {
            System.err.println("[" + sIP + "] 登录设备失败，错误代码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] 设备登录成功！USER_ID：" + mUserId);
        }
        return mUserId;
    }

    // 获取设备状态
    public int getState() {
        HCNetSDK.NET_DVR_WORKSTATE_V30 deviceState = new HCNetSDK.NET_DVR_WORKSTATE_V30();
        boolean success = hCNetSDK.NET_DVR_GetDVRWorkState_V30(mUserId, deviceState);
        if (!success) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] 获取设备状态失败!");
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] 设备状态:" + deviceState.dwDeviceStatic);
            return deviceState.dwDeviceStatic;
        }
        return -1;
    }

    // 获取门禁主机工作状态
    public void getAcsStatus() {
        HCNetSDK.NET_DVR_ACS_WORK_STATUS_V50 netDvrAcsWorkStatusV50 = new HCNetSDK.NET_DVR_ACS_WORK_STATUS_V50();
        netDvrAcsWorkStatusV50.dwSize = netDvrAcsWorkStatusV50.size();
        netDvrAcsWorkStatusV50.write();
        IntByReference intByReference = new IntByReference(0);
        boolean status = hCNetSDK.NET_DVR_GetDVRConfig(mUserId, HCNetSDK.NET_DVR_GET_ACS_WORK_STATUS_V50, 0xFFFFFFFF, netDvrAcsWorkStatusV50.getPointer(), netDvrAcsWorkStatusV50.size(), intByReference);
        if (!status) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] 获取门禁主机工作状态，错误码为：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] 获取门禁主机工作状态成功！");
            netDvrAcsWorkStatusV50.read();
            System.out.println("1.门锁状态（或者梯控的继电器开合状态）：" + netDvrAcsWorkStatusV50.byDoorLockStatus[0]); //门锁状态：0- 正常关，1- 正常开，2- 短路报警，3- 断路报警，4- 异常报警
            System.out.println("2.门状态（或者梯控的楼层状态）：" + netDvrAcsWorkStatusV50.byDoorStatus[0]); //门状态（或者梯控的楼层状态）：1- 休眠，2- 常开状态（对于梯控，表示自由状态），3- 常闭状态（对于梯控，表示禁用状态），4- 普通状态（对于梯控，表示受控状态）
            System.out.println("3.门磁状态：" + netDvrAcsWorkStatusV50.byMagneticStatus[0]); //门磁状态，0-正常关，1-正常开，2-短路报警，3-断路报警，4-异常报警
            System.out.println("4.事件报警输入状态：" + netDvrAcsWorkStatusV50.byCaseStatus[0]); //事件报警输入状态：0- 无输入，1- 有输入
        }
    }

    // 查询人员[模糊搜索]
    public void searchUserInfo(String employeeNo) {
        String dwCommand = "POST /ISAPI/AccessControl/UserInfo/Search?format=json";
        int lHandler = startRemoteConfig(dwCommand, HCNetSDK.NET_DVR_JSON_CONFIG);
        if (lHandler < 0) return;
        // 输入参数，XML或者JSON数据，查询多条人员信息json报文
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("searchID", employeeNo);  // 查询id
        parameter.put("maxResults", 30);        // 最大查询数量
        String strInBuffer = ConfigFileUtil.getReqBodyFromTemplate("SearchUserInfoParam.json", parameter);
        System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 查询报文:" + strInBuffer);
        HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(strInBuffer.length());
        System.arraycopy(strInBuffer.getBytes(), 0, ptrInBuffer.byValue, 0, strInBuffer.length());
        ptrInBuffer.write();
        // 定义接收结果的结构体
        HCNetSDK.BYTE_ARRAY ptrOutBuffer = new HCNetSDK.BYTE_ARRAY(10 * 1024);
        IntByReference pInt = new IntByReference(0);
        while (true) {
            int dwState = sendWithRecvRemoteConfig(lHandler, ptrInBuffer.getPointer(), strInBuffer.length(), ptrOutBuffer.getPointer(), pInt);
            if (dwState == -1) break;
            ptrOutBuffer.read();
            String strResult = new String(ptrOutBuffer.byValue).trim();
            System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 查询人员返回:" + strResult);
            if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 配置等待");
                threadSleep(500);
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 查询人员失败");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 查询人员异常");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 查询人员成功");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchUserInfo 获取人员完成");
                break;
            }
        }
        stopRemoteConfig(lHandler);
    }

    // 添加人员
    public Boolean addUserInfo(String employeeNo, String name, String gender) {
        String dwCommand = "PUT /ISAPI/AccessControl/UserInfo/SetUp?format=json";
        int lHandler = startRemoteConfig(dwCommand, HCNetSDK.NET_DVR_JSON_CONFIG);
        if (lHandler < 0) return false;
        // 输入参数，XML或者JSON数据，下发人员信息json报文，其他参数设置参考conf/acs/AddUserInfoParam.json中报文参数
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("employeeNo", employeeNo);// 员工ID
        parameter.put("name", name);            // 员工名称
        parameter.put("gender", gender);        // 性别
        parameter.put("enable", true);          // 是否启用
        parameter.put("doorNo", 1);             // 门编号
        String inUserJson = ConfigFileUtil.getReqBodyFromTemplate("AddUserInfoParam.json", parameter);
        System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 下发人员参数：" + inUserJson);
        byte[] byInBuffer = inUserJson.getBytes(StandardCharsets.UTF_8);
        HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(byInBuffer.length);
        ptrInBuffer.read();
        System.arraycopy(byInBuffer, 0, ptrInBuffer.byValue, 0, byInBuffer.length);
        ptrInBuffer.write();
        // 定义接收结果的结构体
        HCNetSDK.BYTE_ARRAY ptrOutBuffer = new HCNetSDK.BYTE_ARRAY(1024);
        IntByReference pInt = new IntByReference(0);
        Boolean addResult = false;
        while (true) {
            int dwState = sendWithRecvRemoteConfig(lHandler, ptrInBuffer.getPointer(), byInBuffer.length, ptrOutBuffer.getPointer(), pInt);
            if (dwState == -1) break;
            // 读取返回的json并解析
            ptrOutBuffer.read();
            String strResult = new String(ptrOutBuffer.byValue).trim();
            System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 添加人员返回:" + strResult);
            HiResponse hiResponse = JSON.parseObject(strResult, HiResponse.class);
            int statusCode = hiResponse.getStatusCode();
            if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 配置等待");
                threadSleep(500);
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 下发人员失败");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 下发人员异常");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                // 返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了，但并不代表下发成功，比如有些设备可能因为人员已存在等原因下发失败，所以需要解析Json报文
                if (statusCode != 1) {
                    System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 下发人员成功,但是有异常情况");
                } else {
                    System.out.println("[" + sIP + "] [" + deviceNo + "] addUserInfo 下发人员成功");
                }
                addResult = true;
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                // 发人员时：dwState其实不会走到这里，因为设备不知道我们会下发多少个人，所以长连接需要我们主动关闭
                System.out.println("[" + sIP + "] [" + deviceNo + "] 下发人员完成");
                break;
            }
        }
        stopRemoteConfig(lHandler);
        return addResult;
    }

    // 删除单个人员
    public Boolean deleteUserInfo(String employeeNo) {
        String dwCommand = "PUT /ISAPI/AccessControl/UserInfoDetail/Delete?format=json";
        // 输入参数，XML或者JSON数据，删除人员信息json报文
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("employeeNo", employeeNo);// 员工ID号
        String deleteUserJson = ConfigFileUtil.getReqBodyFromTemplate("DeleteUserInfoParam.json", parameter);
        System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo 参数报文：" + deleteUserJson);
        String result = TransIsapi.put(hCNetSDK, mUserId, dwCommand, deleteUserJson);
        System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo 接口调用成功：" + result);
        // 获取删除进度
        String pcCommand = "GET /ISAPI/AccessControl/UserInfoDetail/DeleteProcess?format=json";
        Boolean delResult = false;
        while (true) {
            String deleteResult = TransIsapi.get(hCNetSDK, mUserId, pcCommand);
            JSONObject jsonObject = JSON.parseObject(deleteResult);
            jsonObject = jsonObject != null ? jsonObject.getJSONObject("UserInfoDetailDeleteProcess") : null;
            String process = jsonObject != null ? jsonObject.getString("status") : null;
            System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo process：" + process);
            if (process == null) break;
            if ("processing".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo 正在删除");
            } else if ("success".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo 删除成功");
                delResult = true;
                break;
            } else if ("failed".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] deleteUserInfo 删除失败");
                break;
            }
        }
        return delResult;
    }

    // 删除所有人员
    public void clearUserInfo() {
        String dwCommand = "GET /ISAPI/AccessControl/UserInfoDetail/DeleteProcess?format=json";
        // 输入参数，XML或者JSON数据，删除人员信息json报文
        String deleteUserJson = ConfigFileUtil.readFileContent("ClearUserInfoParam.json");
        System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo 参数报文：" + deleteUserJson);
        String deleteUserUrl = "PUT /ISAPI/AccessControl/UserInfoDetail/Delete?format=json";
        String result = TransIsapi.put(hCNetSDK, mUserId, deleteUserUrl, deleteUserJson);
        System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo 接口调用成功：" + result);
        // 获取删除进度
        while (true) {
            String deleteResult = TransIsapi.get(hCNetSDK, mUserId, dwCommand);
            JSONObject jsonObject = JSON.parseObject(deleteResult);
            jsonObject = jsonObject != null ? jsonObject.getJSONObject("UserInfoDetailDeleteProcess") : null;
            String process = jsonObject != null ? jsonObject.getString("status") : null;
            System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo process：" + process);
            if (process == null) break;
            if ("processing".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo 正在删除");
            } else if ("success".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo 删除成功");
                break;
            } else if ("failed".equalsIgnoreCase(process)) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] clearUserInfo 删除失败");
                break;
            }
        }
    }

    // 查询人脸信息
    public void searchFaceInfo(String employeeNo) {
        String dwCommand = "POST /ISAPI/Intelligent/FDLib/FDSearch?format=json";
        int lHandler = startRemoteConfig(dwCommand, HCNetSDK.NET_DVR_FACE_DATA_SEARCH);
        if (lHandler < 0) return;
        // 输入参数，XML或者JSON数据，查询人脸图片json报文
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("employeeNo", employeeNo); // 员工ID号
        String strInBuffer = ConfigFileUtil.getReqBodyFromTemplate("SearchFaceInfoParam.json", parameter);
        System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 参数报文：" + strInBuffer);
        HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(strInBuffer.length());
        System.arraycopy(strInBuffer.getBytes(), 0, ptrInBuffer.byValue, 0, strInBuffer.length());
        ptrInBuffer.write();
        // 定义接收结果的结构体
        HCNetSDK.NET_DVR_JSON_DATA_CFG mJsonData = new HCNetSDK.NET_DVR_JSON_DATA_CFG();
        mJsonData.write();
        IntByReference pInt = new IntByReference(0);
        while (true) {
            int dwState = sendWithRecvRemoteConfig(lHandler, ptrInBuffer.getPointer(), strInBuffer.length(), mJsonData.getPointer(), pInt);
            if (dwState == -1) break;
            // 读取返回的json并解析
            mJsonData.read();
            if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 配置等待");
                threadSleep(500);
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 查询人脸失败");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 查询人脸异常");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                // 解析JSON字符串
                HCNetSDK.BYTE_ARRAY pJsonData = new HCNetSDK.BYTE_ARRAY(mJsonData.dwJsonDataSize);
                pJsonData.write();
                Pointer pPlateInfo = pJsonData.getPointer();
                pPlateInfo.write(0, mJsonData.lpJsonData.getByteArray(0, pJsonData.size()), 0, pJsonData.size());
                pJsonData.read();
                String strResult = new String(pJsonData.byValue).trim();
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 查询人脸成功:" + strResult);
                HiEmployee hiEmployee = JSON.parseObject(strResult, HiEmployee.class);
                // 确认有人脸
                if (hiEmployee.getNumOfMatches() != 0) {
                    List<HiEmployee.MatchItem> matchList = hiEmployee.getMatchList();
                    HiEmployee.MatchItem matchItem = matchList.get(0);
                    String FPID = matchItem.getFPID(); //获取json中人脸关联的工号
                    try {
                        // 将字节写入文件
                        FileOutputStream fout = new FileOutputStream(System.getProperty("user.dir") + "\\pic\\" + FPID + ".jpg");
                        ByteBuffer buffers = mJsonData.lpPicData.getByteBuffer(0, mJsonData.dwPicDataSize);
                        byte[] bytes = new byte[mJsonData.dwPicDataSize];
                        buffers.rewind();
                        buffers.get(bytes);
                        fout.write(bytes);
                        fout.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchFaceInfo 获取人脸完成");
                break;
            }
        }
        stopRemoteConfig(lHandler);
    }

    // 下发人脸图片
    public Boolean addFaceInfo(String employeeNo, String faceUrl) {
        // 图片下载
        String localFaceUrl = getLocalFaceUrl(faceUrl);
        if (localFaceUrl == null) return false;

        String dwCommand = "PUT /ISAPI/Intelligent/FDLib/FDSetUp?format=json";
        int lHandler = startRemoteConfig(dwCommand, HCNetSDK.NET_DVR_FACE_DATA_RECORD);
        if (lHandler < 0) return false;
        HCNetSDK.NET_DVR_JSON_DATA_CFG strAddFaceDataCfg = new HCNetSDK.NET_DVR_JSON_DATA_CFG();
        strAddFaceDataCfg.read();
        // 输入参数，XML或者JSON数据，添加人脸图片json报文
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("FDID", "1");
        jsonObject.put("faceLibType", "blackFD");
        jsonObject.put("faceURL", localFaceUrl);
        jsonObject.put("FPID", employeeNo);
        String strJsonData = jsonObject.toString();
        System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 参数报文：" + strJsonData);
        HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(strJsonData.length());
        System.arraycopy(strJsonData.getBytes(), 0, ptrInBuffer.byValue, 0, strJsonData.length());
        ptrInBuffer.write();
        strAddFaceDataCfg.dwSize = strAddFaceDataCfg.size();
        strAddFaceDataCfg.lpJsonData = ptrInBuffer.getPointer();
        strAddFaceDataCfg.dwJsonDataSize = strJsonData.length();
        strAddFaceDataCfg.lpPicData = null;
        strAddFaceDataCfg.dwPicDataSize = 0;
        strAddFaceDataCfg.write();
        // 定义接收结果的结构体
        HCNetSDK.BYTE_ARRAY ptrOutBuffer = new HCNetSDK.BYTE_ARRAY(1024);
        IntByReference pInt = new IntByReference(0);
        Boolean addResult = false;
        while (true) {
            int dwState = sendWithRecvRemoteConfig(lHandler, strAddFaceDataCfg.getPointer(), strAddFaceDataCfg.dwSize, ptrOutBuffer.getPointer(), pInt);
            if (dwState == -1) break;
            // 读取返回的json并解析
            ptrOutBuffer.read();
            String strResult = new String(ptrOutBuffer.byValue).trim();
            System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 添加人脸返回:" + strResult);
            HiResponse hiResponse = JSON.parseObject(strResult, HiResponse.class);
            if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 配置等待");
                threadSleep(500);
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 下发人脸失败");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 下发人脸异常");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                // 返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了，但并不代表下发成功，比如人脸图片不符合设备规范等原因，所以需要解析Json报文
                if (hiResponse.getStatusCode() != 1) {
                    System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 下发人脸成功，但是有异常情况");
                } else {
                    System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 下发人脸成功");
                    addResult = true;
                }
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                // 下发人脸时：dwState其实不会走到这里，因为设备不知道我们会下发多少个人，所以长连接需要我们主动关闭
                System.out.println("[" + sIP + "] [" + deviceNo + "] addFaceInfo 下发人脸完成");
                break;
            }
        }
        stopRemoteConfig(lHandler);
        return addResult;
    }

    // 删除人脸信息，支持批量删除，json中添加多个工号
    public void deleteFaceInfo(String employeeNo) {
        String dwCommand = "PUT /ISAPI/Intelligent/FDLib/FDSearch/Delete?format=json&FDID=1&faceLibType=blackFD";
        // 输入参数，XML或者JSON数据，删除人脸图片json报文
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("employeeNo", employeeNo); // 员工ID号
        String deleteFaceJson = ConfigFileUtil.getReqBodyFromTemplate("DeleteFaceInfoParam.json", parameter);
        System.out.println("[" + sIP + "] [" + deviceNo + "] deleteFaceInfo 删除人脸图片参数：" + deleteFaceJson);
        String result = TransIsapi.put(hCNetSDK, mUserId, dwCommand, deleteFaceJson);
        System.out.println("[" + sIP + "] [" + deviceNo + "] deleteFaceInfo 删除人员结果：" + result);
    }

    // 门禁事件查询，基于起止时间，事件类型进行查询
    public void searchEvent(int dwMajor, int dwMinor, String startTime, String endTime) {
        System.out.println("[" + sIP + "] [" + deviceNo + "] searchEvent 门禁事件查询 dwMajor:" + dwMajor + ", dwMinor:" + dwMinor + ", startTime:" + startTime + ", endTime:" + endTime);
        HCNetSDK.NET_DVR_ACS_EVENT_COND strAcsEventCond = new HCNetSDK.NET_DVR_ACS_EVENT_COND();
        strAcsEventCond.read();
        strAcsEventCond.dwSize = strAcsEventCond.size();
        // 查询全部主次类型的报警
        strAcsEventCond.dwMajor = dwMajor;  //主事件类型，0代表查询所有事件
        strAcsEventCond.dwMinor = dwMinor;  //次事件类型，根据主事件类型选择
        // 开始时间
        if (startTime != null) {
            Calendar startCalendar = TimerUtils.stringToCalendar(startTime);
            strAcsEventCond.struStartTime.dwYear = startCalendar.get(Calendar.YEAR);
            strAcsEventCond.struStartTime.dwMonth = startCalendar.get(Calendar.MONTH) + 1;
            strAcsEventCond.struStartTime.dwDay = startCalendar.get(Calendar.DAY_OF_MONTH);
            strAcsEventCond.struStartTime.dwHour = startCalendar.get(Calendar.HOUR_OF_DAY);
            strAcsEventCond.struStartTime.dwMinute = startCalendar.get(Calendar.MINUTE);
            strAcsEventCond.struStartTime.dwSecond = startCalendar.get(Calendar.SECOND);
        }
        // 结束时间
        if (endTime != null) {
            Calendar endCalendar = TimerUtils.stringToCalendar(endTime);
            strAcsEventCond.struEndTime.dwYear = endCalendar.get(Calendar.YEAR);
            strAcsEventCond.struEndTime.dwMonth = endCalendar.get(Calendar.MONTH) + 1;
            strAcsEventCond.struEndTime.dwDay = endCalendar.get(Calendar.DAY_OF_MONTH);
            strAcsEventCond.struEndTime.dwHour = endCalendar.get(Calendar.HOUR_OF_DAY);
            strAcsEventCond.struEndTime.dwMinute = endCalendar.get(Calendar.MINUTE);
            strAcsEventCond.struEndTime.dwSecond = endCalendar.get(Calendar.SECOND);
        }
        strAcsEventCond.wInductiveEventType = 1;
        strAcsEventCond.byPicEnable = 1;//是否带图片，0-不带图片，1-带图片
        strAcsEventCond.write();
        int lHandler = startRemoteConfig(HCNetSDK.NET_DVR_GET_ACS_EVENT, strAcsEventCond.getPointer(), strAcsEventCond.size());
        if (lHandler < 0) return;
        HCNetSDK.NET_DVR_ACS_EVENT_CFG strAcsEventCfg = new HCNetSDK.NET_DVR_ACS_EVENT_CFG();
        strAcsEventCfg.read();
        strAcsEventCfg.dwSize = strAcsEventCfg.size();
        strAcsEventCfg.write();
        Pointer ptrStrEventCfg = strAcsEventCfg.getPointer();
        while (true) {
            int dwEventSearch = hCNetSDK.NET_DVR_GetNextRemoteConfig(lHandler, ptrStrEventCfg, strAcsEventCfg.size());
            if (dwEventSearch <= -1) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] HCNetSDK NET_DVR_GetNextRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            } else if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_NEED_WAIT) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchEvent 配置等待");
                threadSleep(500);
            } else if (dwEventSearch == HCNetSDK.NET_SDK_NEXT_STATUS__FINISH) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchEvent 获取事件完成");
                break;
            } else if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_FAILED) {
                System.out.println("[" + sIP + "] [" + deviceNo + "] searchEvent 获取事件出现异常");
                break;
            } else if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_SUCCESS) {
                strAcsEventCfg.read();
                // 获取的事件信息通过strAcsEventCfg结构体进行解析，工号有两个字段，dwEmployeeNo表示工号，当值为0时，表示无效，解析byEmployeeNo参数
                String employeeNo = new String(strAcsEventCfg.struAcsEventInfo.byEmployeeNo).trim();
                String strTime = strAcsEventCfg.struTime.dwYear + "-" + strAcsEventCfg.struTime.dwMonth + "-" + strAcsEventCfg.struTime.dwDay + " " + strAcsEventCfg.struTime.dwHour + ":" + strAcsEventCfg.struTime.dwMinute + ":" + strAcsEventCfg.struTime.dwSecond;
                System.out.println("[" + sIP + "] [" + deviceNo + "] 获取事件成功 dwMajor:" + strAcsEventCfg.dwMajor + ", dwMinor:" + strAcsEventCfg.dwMinor + ", employeeNo:" + employeeNo + ", time:" + strTime);
                // 对人脸认证通过的事件保存记录
                if (dwMajor == 5 && dwMinor == 75) alarmCallBack.savePickRecord(deviceNo, employeeNo, strTime);
            }
        }
        stopRemoteConfig(lHandler);
    }

    // 远程控门
    public void controlGateway(int dwStaic) {
        /*
         * lGatewayIndex：门禁序号（楼层编号、锁ID），从1开始，-1表示对所有门（或者梯控的所有楼层）进行操作
         * dwStaic：命令值：0-关闭（对于梯控，表示受控），1-打开（对于梯控，表示开门），2-常开（对于梯控，表示自由、通道状态），3-常关（对于梯控，表示禁用），4-恢复（梯控，普通状态），5-访客呼梯（梯控），6-住户呼梯（梯控）
         */
        if (!hCNetSDK.NET_DVR_ControlGateway(mUserId, 1, dwStaic)) {
            System.out.println("[" + sIP + "] [" + deviceNo + "] controlGateway 远程控门失败：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] controlGateway 远程控门成功");
        }
    }

    // 登出操作
    public void logout() {
        if (!hCNetSDK.NET_DVR_Logout(mUserId)) {
            System.err.println("[" + sIP + "] [" + deviceNo + "] HCNetSDK 设备注销失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println("[" + sIP + "] [" + deviceNo + "] HCNetSDK 设备注销成功！");
        }
        mUserId = -1;
        mAlarmHandle = -1;
    }

    // 线程等待
    private void threadSleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public String getDeviceNo() {
        return deviceNo;
    }

    /**
     * 获取本地图片地址
     *
     * @param url
     * @return
     */
    public String getLocalFaceUrl(String url) {
        // 生成本地临时图片
        // 使用 lastIndexOf 找到最后一个 '/' 的位置
        int lastSlashIndex = url.lastIndexOf('/');
        // 使用 substring 提取文件名
        String fileName = url.substring(lastSlashIndex + 1);
        // 设置本地路径
        String localUrl = System.getProperty("user.dir") + "/static/pic/" + fileName;
        // 下载图片到本地
        if (!ImgUtils.generateLocalImage(url, localUrl)) {
            return null;
        }
        // 配置本地化静态资源目录
        return localFaceUrl + fileName;
    }

    /**
     * 设置url
     * @param Url
     */
    public void setLocalFaceUrl(String Url){
        if (Url != null){
            this.localFaceUrl = Url;
        }
    }
}
